﻿using System;

namespace Utk
{
    class Maybe<T>
    {
        public Maybe(T Value)
        {
            _value = Value;
            _hasValue = true;
        }

        public Maybe()
        {
            _hasValue = false;
        }

        public T Value
        {
            get
            {
                if (!_hasValue)
                {
                    throw new InvalidOperationException();
                }
                return _value;
            }

        }

        public bool HasValue
        {
            get { return _hasValue; }
        }

        public static implicit operator Maybe<T>(T value)
        {
            return new Maybe<T>(value);
        }

        public static implicit operator T(Maybe<T> maybe)
        {
            return maybe.Value;
        }

        public static readonly Maybe<T> Nothing = new Maybe<T>();

        private readonly bool _hasValue;
        private readonly T _value;
    }

    static class MaybeExtensions
    {
        public static void Apply<T>(this Maybe<T> value, Action<T> action)
        {
            if (value.HasValue)
            {
                action(value);
            }
        }

        public static Maybe<TR> Apply<T, TR>(this Maybe<T> value, Func<T, TR> func)
        {
            if (value.HasValue)
            {
                return func(value);
            }
            return Maybe<TR>.Nothing;
        }

        public static void Apply<T>(this Maybe<T> value, Action<T> action, Action fallbackAction)
        {
            if (value.HasValue)
            {
                action(value);
            }
            else
            {
                fallbackAction();
            }
        }

        public static TR Apply<T, TR>(this Maybe<T> value, Func<T, TR> func, Func<TR> fallbackFunc)
        {
            if (value.HasValue)
            {
                return func(value);
            }
            else
            {
                return fallbackFunc();
            }
        }
    }
}